package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.activity.client.ActivityFeignClient;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.activity.ActivityRule;
import com.atguigu.gmall.model.activity.CouponInfo;
import com.atguigu.gmall.model.enums.ActivityType;
import com.atguigu.gmall.model.enums.CouponType;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.*;
import com.atguigu.gmall.order.mapper.*;
import com.atguigu.gmall.order.service.OrderService;
import com.atguigu.gmall.order.util.OrderUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.bouncycastle.util.Integers;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * @author mqx
 * @date 2020-11-18 14:20:38
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private OrderDetailActivityMapper orderDetailActivityMapper;

    @Autowired
    private OrderDetailCouponMapper orderDetailCouponMapper;

    @Autowired
    private OrderStatusLogMapper orderStatusLogMapper;

    @Autowired
    private ActivityFeignClient activityFeignClient;

    @Override
    @Transactional
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //  orderDetail
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //  orderInfo
        //  总金额，订单状态，userId, out_trade_no ,订单描述，创建时间，过期时间, 进度状态

        //  订单状态
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //  第三方交易编号
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + "" + new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        //  利用商品的名称，组成订单描述
        //  描述订单的
        orderInfo.setTradeBody("冬天冷了，买个雕! 暖和暖和!");
        //  创建时间
        orderInfo.setCreateTime(new Date());
        //  过期时间 当前系统时间+1天
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE,1);
        orderInfo.setExpireTime(calendar.getTime());
        //  运费
        orderInfo.setFeightFee(new BigDecimal("0"));
        //  操作时间
        orderInfo.setOperateTime(orderInfo.getCreateTime());
        //  进度状态
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());

        //  获取促销金额
        BigDecimal activityReduceAmount = this.getActivityReduceAmount(orderInfo);
        orderInfo.setActivityReduceAmount(activityReduceAmount);

        orderInfo.sumTotalAmount();
        orderInfoMapper.insert(orderInfo);

        // 计算购物项分摊的优惠减少金额，按比例分摊，退款时按实际支付金额退款
        Map<String, BigDecimal> skuIdToReduceAmountMap = OrderUtil.computeOrderDetailPayAmount(orderInfo);
        // sku对应的订单明细
        Map<Long, Long> skuIdToOrderDetailIdMap = new HashMap<>();

        //  判断
        if (!CollectionUtils.isEmpty(orderDetailList)){
            //  循环遍历
            for (OrderDetail orderDetail : orderDetailList) {
                //  设置订单Id
                orderDetail.setOrderId(orderInfo.getId());
                orderDetail.setCreateTime(new Date());
                // 促销活动分摊金额
                BigDecimal splitActivityAmount = skuIdToReduceAmountMap.get("activity:"+orderDetail.getSkuId());
                if(null == splitActivityAmount) {
                    splitActivityAmount = new BigDecimal(0);
                }
                orderDetail.setSplitActivityAmount(splitActivityAmount);

                //优惠券分摊金额
                BigDecimal splitCouponAmount = skuIdToReduceAmountMap.get("coupon:"+orderDetail.getSkuId());
                if(null == splitCouponAmount) {
                    splitCouponAmount = new BigDecimal(0);
                }
                orderDetail.setSplitCouponAmount(splitCouponAmount);

                // 优惠后的总金额
                BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
                BigDecimal payAmount = skuTotalAmount.subtract(splitActivityAmount).subtract(splitCouponAmount);
                orderDetail.setSplitTotalAmount(payAmount);
                orderDetailMapper.insert(orderDetail);
                //  skuId 对应的订单明细Id
                skuIdToOrderDetailIdMap.put(orderDetail.getSkuId(), orderDetail.getId());
            }
        }
        //记录订单与促销活动和优惠券的关联信息
        this.saveActivityAndCouponRecord(orderInfo, skuIdToOrderDetailIdMap);

        //记录订单状态
        this.saveOrderStatusLog(orderInfo.getId(), orderInfo.getOrderStatus());

        //  返回订单Id
        return orderInfo.getId();
    }

    /**
     * 获取活动总金额
     * @param orderInfo
     * @return
     */
    private BigDecimal getActivityReduceAmount(OrderInfo orderInfo) {
        //  促销优惠金额
        BigDecimal activityReduceAmount = new BigDecimal("0");
        //  获取分组之后的集合
        List<OrderDetailVo> orderDetailVoList = orderInfo.getOrderDetailVoList();
        if(!CollectionUtils.isEmpty(orderDetailVoList)) {
            //  循环遍历
            for(OrderDetailVo orderDetailVo : orderDetailVoList) {
                //  获取优惠规则
                ActivityRule activityRule = orderDetailVo.getActivityRule();
                //  判断优惠规则
                if(null != activityRule) {
                    activityReduceAmount = activityReduceAmount.add(activityRule.getReduceAmount());
                }
            }
        }
        return activityReduceAmount;
    }

    /**
     * 记录订单与促销活动和优惠券的关联信息
     * @param orderInfo
     * @param skuIdToOrderDetailIdMap
     */
    private void saveActivityAndCouponRecord(OrderInfo orderInfo, Map<Long, Long> skuIdToOrderDetailIdMap) {
        //记录促销活动
        List<OrderDetailVo> orderDetailVoList = orderInfo.getOrderDetailVoList();
        //  判断不为空
        if(!CollectionUtils.isEmpty(orderDetailVoList)) {
            //  循环遍历
            for(OrderDetailVo orderDetailVo : orderDetailVoList) {
                //  获取到规则，保存订单与活动的表数据
                ActivityRule activityRule = orderDetailVo.getActivityRule();
                if(null != activityRule) {
                    for(Long skuId : activityRule.getSkuIdList()) {
                        OrderDetailActivity orderDetailActivity = new OrderDetailActivity();
                        orderDetailActivity.setOrderId(orderInfo.getId());
                        orderDetailActivity.setOrderDetailId(skuIdToOrderDetailIdMap.get(skuId));
                        orderDetailActivity.setActivityId(activityRule.getActivityId());
                        orderDetailActivity.setActivityRule(activityRule.getId());
                        orderDetailActivity.setSkuId(skuId);
                        orderDetailActivity.setCreateTime(new Date());
                        orderDetailActivityMapper.insert(orderDetailActivity);
                    }
                }
            }
        }

        //  记录优惠券
        //  是否更新优惠券状态  如果当前一个 1，3，5 都使用了这个优惠劵，则我们只需要更新一次即可！
        //  mq
        Boolean isUpdateCouponStatus = false;
        CouponInfo couponInfo = orderInfo.getCouponInfo();
        if(null != couponInfo) {
            //  获取到当前优惠劵对应的skuId
            List<Long> skuIdList = couponInfo.getSkuIdList();
            for (Long skuId : skuIdList) {
                OrderDetailCoupon orderDetailCoupon = new OrderDetailCoupon();
                orderDetailCoupon.setOrderId(orderInfo.getId());
                orderDetailCoupon.setOrderDetailId(skuIdToOrderDetailIdMap.get(skuId));
                orderDetailCoupon.setCouponId(couponInfo.getId());
                orderDetailCoupon.setSkuId(skuId);
                orderDetailCoupon.setCreateTime(new Date());
                orderDetailCouponMapper.insert(orderDetailCoupon);

                // 更新优惠券使用状态
                if(!isUpdateCouponStatus) {
                    //  发送消息： 接收消息处理的时候，失败了，你怎么做的？
                    activityFeignClient.updateCouponInfoUseStatus(couponInfo.getId(), orderInfo.getUserId(), orderInfo.getId());
                }
                isUpdateCouponStatus = true;
            }
        }
    }
    @Override
    public String getTradeNo(String userId) {
        //  使用UUID 创建一个流水号
        String tradeNo = UUID.randomUUID().toString();
        //  必须先获取到key
        String tradeNoKey = "user:" + userId + ":tradeCode";
        //  将流水号放入缓存
        redisTemplate.opsForValue().set(tradeNoKey,tradeNo);

        return tradeNo;
    }



    @Override
    public boolean checkTradeCode(String userId, String tradeNo) {
        //  必须先获取到key
        String tradeNoKey = "user:" + userId + ":tradeCode";
        //  获取缓存的流水号
        String tradeNoRedis = (String) redisTemplate.opsForValue().get(tradeNoKey);
        //  返回比较结果
        return tradeNo.equals(tradeNoRedis);
    }

    @Override
    public void deleteTradeNo(String userId) {
        //  必须先获取到key
        String tradeNoKey = "user:" + userId + ":tradeCode";
        //  删除
        redisTemplate.delete(tradeNoKey);
    }

    @Override
    public boolean checkStock(Long skuId, Integer skuNum) {
        //  service-order 调用ware-manage 需要远程调用，使用httpClient
        //  传入远程调用的地址 http://localhost:9001/hasStock?skuId=10221&num=2
        //  远程调用接口 返回值 0：无库存   1：有库存
        String result = HttpClientUtil.doGet("http://localhost:9001/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  返回判断结果
        return "1".equals(result);
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        //  关闭订单
        //  update order_info set order_status=CLOSED , process_status = CLOSED where id = orderId;
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        orderInfo.setOrderStatus(ProcessStatus.CLOSED.getOrderStatus().name());
        //        //  orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //        //  在进度状态ProcessStatus 中能够获取订单的状态！
        //        orderInfoMapper.updateById(orderInfo);

        //  封装一个方法将 orderId，ProcessStatus.CLOSED 作为参数,后面的业务会经常用到更新。
        updateOrderStatus(orderId, ProcessStatus.CLOSED);

        //  发送消息关闭交易记录 paymentInfo ;
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
    }

    //  更新订单！
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setProcessStatus(processStatus.name());
        //  在进度状态ProcessStatus 中能够获取订单的状态！
        orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  select * from order_info where id = orderId;
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo!=null){
            //  查询订单明细
            QueryWrapper<OrderDetail> orderDetailQueryWrapper = new QueryWrapper<>();
            orderDetailQueryWrapper.eq("order_id",orderId);
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(orderDetailQueryWrapper);
            //  赋值给订单主表
            orderInfo.setOrderDetailList(orderDetailList);
        }

        return orderInfo;
    }

    @Override
    public void sendOrderStatus(Long orderId) {
        //  发送消息之前：更新订单状态
        updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);

        //  获取发送消息内容：
        String wareJson = initWareOrder(orderId);
        //  发送消息
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK,wareJson);

    }

    //  通过orderId 将发送的数据封装成功Json - String
    private String initWareOrder(Long orderId) {
        //  Json 字符串组成 是由orderInfo 中的部分组成！
        //  先获取到OrderInfo
        OrderInfo orderInfo = getOrderInfo(orderId);
        //  将这个 orderInfo 中组成Json 的字符串的字段，封装到一个Map 中。
        Map map = initWareOrder(orderInfo);
        //  将map 转换为Json 字符串
        return JSON.toJSONString(map);
    }

    //  将orderInfo 部分字段转换为Map
    public Map initWareOrder(OrderInfo orderInfo) {
        //  先声明一个map 集合
        Map<String,Object> map = new HashMap();
        map.put("orderId",orderInfo.getId());
        map.put("consignee",orderInfo.getConsignee());
        map.put("consigneeTel", orderInfo.getConsigneeTel());
        map.put("orderComment", orderInfo.getOrderComment());
        map.put("orderBody", orderInfo.getTradeBody());
        map.put("deliveryAddress", orderInfo.getDeliveryAddress());
        map.put("paymentWay", "2");
        map.put("wareId", orderInfo.getWareId());// 仓库Id ，减库存拆单时需要使用！

        //  声明一个集合来存储orderDetailMap
        List<HashMap> orderDetailArrayList = new ArrayList<>();
        //  所有的数据都是来自于订单明细：OrderDetail;
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        for (OrderDetail orderDetail : orderDetailList) {
            HashMap<String, Object> orderDetailMap = new HashMap<>();
            orderDetailMap.put("skuId",orderDetail.getSkuId());
            orderDetailMap.put("skuNum",orderDetail.getSkuNum());
            orderDetailMap.put("skuName",orderDetail.getSkuName());
            //  将orderDetailMap 存储起来
            orderDetailArrayList.add(orderDetailMap);
        }

        //  [{skuId:101,skuNum:1,skuName:’小米手64G’},{skuId:201,skuNum:1,skuName:’索尼耳机’}]
        map.put("details",orderDetailArrayList);
        //  返回map集合
        return map;
    }

    @Override
    @Transactional
    public List<OrderInfo> orderSplit(long orderId, String wareSkuMap) {
        ArrayList<OrderInfo> orderInfoArrayList = new ArrayList<>();
        /*
        1.  获取原始订单，记录的是哪个订单要拆单
        2.  wareSkuMap 记录的是哪个仓库对应的skuId [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]
            将 wareSkuMap 转换为我们能够操作的对象。
        3.  创建新的子订单，并赋值
        4.  保存新的子订单
        5.  修改原始订单状态 SPLIT
         */
        //  声明一个集合来存储子订单
        List<OrderInfo> orderInfoList = new ArrayList<>();
        OrderInfo orderInfoOrigin = getOrderInfo(orderId);
        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        if (!CollectionUtils.isEmpty(mapList)){
            //  循环遍历
            for (Map map : mapList) {
                //  {"wareId":"1","skuIds":["2","10"]}
                String wareId = (String) map.get("wareId");

                //  获取对应的skuId
                List<String> skuIdList = (List<String>) map.get("skuIds");
                OrderInfo subOrderInfo = new OrderInfo();
                //  属性拷贝
                BeanUtils.copyProperties(orderInfoOrigin,subOrderInfo);
                //  订单Id ，总金额，父Id，仓库Id
                subOrderInfo.setId(null);
                subOrderInfo.setParentOrderId(orderId);
                subOrderInfo.setWareId(wareId);
                // 子订单号
                String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + "" + new Random().nextInt(1000);
                subOrderInfo.setOutTradeNo(outTradeNo);

                // 计算子订单的金额: 必须有订单明细
                // 获取到子订单明细
                // 声明一个集合来存储子订单明细
                ArrayList<OrderDetail> subOrderDetailList = new ArrayList<>();

                List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList();
                // 表示主订单明细中获取到子订单的明细
                if (orderDetailList != null && orderDetailList.size() > 0) {
                    for (OrderDetail orderDetail : orderDetailList) {
                        // 获取子订单明细的商品Id
                        for (String skuId : skuIdList) {
                            if (Long.parseLong(skuId) == orderDetail.getSkuId().longValue()) {
                                OrderDetail subOrderDetail = new OrderDetail();
                                BeanUtils.copyProperties(orderDetail, subOrderDetail);
                                subOrderDetail.setId(null);
                                // 将订单明细添加到集合
                                subOrderDetailList.add(subOrderDetail);
                                //  subOrderDetailList.add(orderDetail); ?
                            }
                        }
                    }
                }
                subOrderInfo.setOrderDetailList(subOrderDetailList);

                // 重新计算子订单和订单明细金额
                //  记录实际支付的总金额
                BigDecimal totalAmount = new BigDecimal("0");
                //  记录原始总金额
                BigDecimal originalTotalAmount = new BigDecimal("0");
                BigDecimal couponAmount = new BigDecimal("0");
                BigDecimal activityReduceAmount = new BigDecimal("0");
                for (OrderDetail subOrderDetail : subOrderDetailList) {
                    //  获取单独一个订单明细总金额
                    BigDecimal skuTotalAmount = subOrderDetail.getOrderPrice().multiply(new BigDecimal(subOrderDetail.getSkuNum()));
                    //  获取订单明细总原始金额
                    originalTotalAmount = originalTotalAmount.add(skuTotalAmount);

                    //  优惠劵总金额
                    couponAmount = couponAmount.add(subOrderDetail.getSplitCouponAmount());
                    //  获取活动总金额
                    activityReduceAmount = activityReduceAmount.add(subOrderDetail.getSplitActivityAmount());

                    //  获取到实际支付总金额
                    totalAmount = totalAmount.add(skuTotalAmount).subtract(subOrderDetail.getSplitCouponAmount()).subtract(subOrderDetail.getSplitActivityAmount());
                    //  totalAmount = totalAmount.add(skuTotalAmount).subtract(couponAmount).subtract(activityReduceAmount);

                }
                subOrderInfo.setTotalAmount(totalAmount);
                subOrderInfo.setOriginalTotalAmount(originalTotalAmount);
                subOrderInfo.setCouponAmount(couponAmount);
                subOrderInfo.setActivityReduceAmount(activityReduceAmount);
                subOrderInfo.setFeightFee(new BigDecimal(0));
                orderInfoMapper.insert(subOrderInfo);

                //保存子订单明细
                for (OrderDetail subOrderDetail : subOrderDetailList) {
                    subOrderDetail.setOrderId(subOrderInfo.getId());
                    orderDetailMapper.insert(subOrderDetail);
                }

                //保存订单状态记录
                List<OrderStatusLog> orderStatusLogList = orderStatusLogMapper.selectList(new QueryWrapper<OrderStatusLog>().eq("order_id", orderId));
                for(OrderStatusLog orderStatusLog : orderStatusLogList) {
                    OrderStatusLog subOrderStatusLog = new OrderStatusLog();
                    BeanUtils.copyProperties(orderStatusLog, subOrderStatusLog);
                    subOrderStatusLog.setId(null);
                    subOrderStatusLog.setOrderId(subOrderInfo.getId());
                    orderStatusLogMapper.insert(subOrderStatusLog);
                }

                // 将子订单添加到集合中！
                orderInfoArrayList.add(subOrderInfo);
            }
        }
        //  修改订单状态
        updateOrderStatus(orderId,ProcessStatus.SPLIT);
        //  子订单集合
        return orderInfoArrayList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        //  封装一个方法将 orderId，ProcessStatus.CLOSED 作为参数,后面的业务会经常用到更新。
        updateOrderStatus(orderId, ProcessStatus.CLOSED);
        //  如果有交易记录则关闭交易记录。
        if ("2".equals(flag)){
            //  发送消息关闭交易记录 paymentInfo ;
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }
    }

    @Override
    public void saveOrderStatusLog(Long orderId, String orderStatus) {
        //记录订单状态
        OrderStatusLog orderStatusLog = new OrderStatusLog();
        orderStatusLog.setOrderId(orderId);
        orderStatusLog.setOrderStatus(orderStatus);
        orderStatusLog.setOperateTime(new Date());
        orderStatusLogMapper.insert(orderStatusLog);
    }

}


